import { Base } from './base'
import { Clip } from './clip'
import { LinkList, TimeRange } from './../types'

const LINK_SIZE = 3
function updateVideoElement(video: HTMLVideoElement, clip: any): void {
  video.setAttribute('crossOrigin', '')
  video.load()
  video.width = 640
  video.height = 360
  if (clip && clip.ref) {
    video.setAttribute('src', clip.ref.url)
    video.setAttribute('data-id', clip.id)
    video.currentTime = clip.ref.in
  }

  // video.src && document.body.append(video)
}

function createVideoElement() {
  const video = document.createElement('video')
  video.setAttribute('preload', 'preload')
  return video
}

function createEmptyCircleLinkList(size: number): LinkList<HTMLVideoElement> {
  const first: LinkList<HTMLVideoElement> = {
    data: createVideoElement(),
    prev: null,
    next: null,
  }
  let last = first
  for (let i = 1; i < size; i++) {
    const cur: LinkList<HTMLVideoElement> = {
      prev: last,
      data: createVideoElement(),
      next: null,
    }
    last.next = cur
    last = cur
  }
  if (last !== first) {
    first.prev = last
    last.next = first
  }
  return first
}

function getTimeRanges(clips: Array<Clip>): Array<TimeRange> {
  let range: TimeRange = {
    start: 0,
    end: 0,
  }
  const ret = []
  for (const i of clips) {
    if (range.end === i.offset) {
      range.end += i.duration
    } else {
      !range.end && ret.push(range)
      range = {
        start: i.offset,
        end: i.offset + i.duration,
      }
    }
  }
  return ret
}

function getIndex(clips: Array<Clip>, time: number): number {
  for (let index = 0; index < clips.length; index++) {
    const clip = clips[index]
    if (clip.offset <= time && clip.offset + clip.duration >= time) {
      return index
    }
  }
  return -1
}

export class Track extends Base {
  protected _clips: Array<Clip>
  protected _currentTime = 0
  protected _duration = 0
  protected _playbackRate = 1
  protected _afId = -1
  public view: HTMLCanvasElement | null = null
  protected _fps = 30
  protected _type = -1
  protected _paused = true
  constructor(options: any) {
    super()
    this._clips = options.children.map((c: any) => new Clip(c))
    this.duration = this._clips
      .map((i) => i.duration)
      .reduce((i, j) => i + j, 0)
  }

  splice(index: number, range: number, ...clips: Clip[]) {
    // check Index Clip, 修改后面clip的offset
    this._clips.splice(index, range, ...clips)
    this.duration = this._clips
      .map((i) => i.duration)
      .reduce((i, j) => i + j, 0)
    this.emit('change', this)
  }

  toJson() {
    return {
      type: this._type,
      children: this._clips.map((i) => i.toJson()),
    }
  }

  get currentTime() {
    return this._currentTime
  }

  set currentTime(val: number) {
    this._currentTime = val
  }

  set duration(val: number) {
    this._duration = val
    this.emit('durationchange', this)
  }
  get duration() {
    return this._duration
  }

  get clips() {
    return this._clips
  }

  get canplay() {
    return true
  }

  onTimeupdate(val: number) {
    this.currentTime = val
  }

  play() {
    this._paused = false
  }

  pause() {
    this._paused = true
  }
}

export class TextTrack extends Track {}

export class ImageTrack extends Track {}

// 是否需要补全空clip
export class VideoTrack extends Track {
  private _el: LinkList<HTMLVideoElement>
  private _currentIndex = -999
  private _videoPaused = true
  private _disabled = false
  private _timeRanges: Array<TimeRange> = []
  constructor(options: any) {
    super(options)
    this._el = createEmptyCircleLinkList(LINK_SIZE)
    this._timeRanges = getTimeRanges(this._clips)
    this.switchToClip(0)
    const canvas = document.createElement('canvas')
    canvas.width = 640
    canvas.height = 360
    this.view = canvas
    const context = canvas.getContext('2d')
    const draw = () => {
      context!.drawImage(this._el.data, 0, 0, 640, 360)
      // cache 画面然后做声画同步
      this._cacheView()
      this._afId = requestAnimationFrame(draw)
    }
    draw()
  }

  // cache
  _cacheView() {}

  // 这里改成变量存储
  private get virtualClips() {
    const clips: Array<Clip> = []
    let offset = 0
    this._clips.forEach((i) => {
      if (i.offset !== offset) {
        clips.push(
          new Clip({
            offset: offset,
            duration: i.offset - offset,
          })
        )
      }
      clips.push(i)
      offset = i.offset + i.duration
    })
    clips.push(new Clip({ offset: offset, duration: Infinity }))
    return clips
  }

  set currentTime(val: number) {
    this._currentTime = val
    // this._el.data.currentTime = val
    this.emit('seeked', this)
  }

  get currentTime() {
    return this._currentTime
  }

  private _bindNativeEvents() {
    this._el.data.addEventListener('timeupdate', this._timeupdate.bind(this))
    // this._el.data.addEventListener('waiting', this._waiting.bind(this))
    this._el.data.addEventListener('canplay', this._canplay.bind(this))
  }

  private _unbindNativeEvents() {
    this._el.data.removeEventListener('timeupdate', this._timeupdate)
    // this._el.data.removeEventListener('waiting', this._waiting)
    this._el.data.removeEventListener('canplay', this._canplay)
  }

  play() {
    this._paused = false
    // this._el.prev?.data.pause()
    // this._el.next?.data.pause()
    return this._el.data && this._el.data.play()
  }

  pause() {
    this._paused = true
    return this._el.data && this._el.data.pause()
  }

  private _timeupdate(e: Event) {
    this.emit('timeupdate', this)
  }

  private _waiting(e: Event) {
    this.emit('waiting', this)
  }

  private _canplay(e: Event) {
    this.emit('canplay', this)
  }

  onTimeupdate(val: number) {
    this.currentTime = val
    // check time  video  然后播放和切换
    this.schedule()
  }

  schedule() {
    const index = getIndex(this.virtualClips, this.currentTime)
    if (this._currentIndex !== index) {
      this.switchToClip(index)
    }
    if (this.virtualClips[index].name && this._paused) {
      const time = this.currentTime - this.virtualClips[index].offset
      this._el.data.currentTime = time + this.virtualClips[index].ref!.in
    }
  }

  switchToClip(index: number) {
    if (this._el.data.src) {
      this._unbindNativeEvents()
      if (!this._paused) {
        this._el.data.pause()
      }
    }

    if (index !== -1) {
      const diff = this._currentIndex - index
      const clip = this.virtualClips[index]
      const nextClip = this.virtualClips[index + 1]
      const prevClip = this.virtualClips[index - 1]
      if (Math.abs(diff) === 1) {
        // near clip
        if (diff > 0) {
          this._el = this._el.prev as LinkList<HTMLVideoElement>
          updateVideoElement(this._el.prev!.data, prevClip)
        } else {
          this._el = this._el.next as LinkList<HTMLVideoElement>
          updateVideoElement(this._el.next!.data, nextClip)
        }
      } else {
        this.preloadMedia()
      }
      if (this._el.data.src) {
        this._bindNativeEvents()
        if (!this._paused) {
          this._el.data.currentTime =
            this.currentTime - clip.offset + clip.ref!.in
          console.log(this._el.data)
          this._el.data.play()
        }
      }
    } else {
      // shut down
    }

    this._currentIndex = index
  }

  preloadMedia() {
    const index = getIndex(this.virtualClips, this.currentTime)
    const clip = this.virtualClips[index]
    const nextClip = this.virtualClips[index + 1]
    const prevClip = this.virtualClips[index - 1]
    updateVideoElement(this._el.prev!.data, prevClip)
    updateVideoElement(this._el.data, clip)
    updateVideoElement(this._el.next!.data, nextClip)
  }
  splice(index: number, range: number, ...clips: Clip[]) {
    super.splice(index, range, ...clips)
    this.preloadMedia()
  }
}
